#include <QTcpSocket>
#include <QFile>
#include <QString>
#include <QStringList>

#include "pop3invoker.h"

POP3Invoker::POP3Invoker()
{
    socket = new QTcpSocket();
}

POP3Invoker::~POP3Invoker()
{
    delete socket;
}

bool POP3Invoker::Connect(QString server,unsigned short port)
{
    socket->connectToHost(server,port);
    if (!socket->waitForConnected())
    {
        qDebug("Could not connect.");
        return false;
    }
    if (!socket->waitForReadyRead())
    {
        qDebug("Could not read.");
        return false;
    }
    QString result = socket->readAll();
    qDebug(qPrintable(result));
    return result.startsWith("+OK");
}

bool POP3Invoker::Login(QString username, QString password)
{
    execute(QString("USER %1\r\n").arg(username));
    return execute(QString("PASS %1\r\n").arg(password));
}

bool POP3Invoker::Statistics(quint64 &numberOfMails,quint64 &totalMailSize)
{
    QString output;
    if(!execute("STAT\r\n",&output))
    {
        return false;
    }
    QStringList separatedOutput = output.split(' ',QString::SkipEmptyParts);
    if (separatedOutput.size() == 3  && output.startsWith("+OK"))
    {
        bool numberOfMailsConversionResult,totalMailSizeConversionResult;
        numberOfMails = separatedOutput.at(1).toULongLong(&numberOfMailsConversionResult);
        totalMailSize = separatedOutput.at(2).toULongLong(&totalMailSizeConversionResult);
        return numberOfMailsConversionResult && totalMailSizeConversionResult;
    }
    else
    {
        return false;
    }
}

bool POP3Invoker::Reset()
{
    return execute("RSET\r\n");
}

bool POP3Invoker::NoOperation()
{
    return execute("NOOP\r\n");
}

bool POP3Invoker::TemporaryDelete(quint64 mailID)
{
    return execute(QString("DELE %1\r\n").arg(mailID));
}

bool POP3Invoker::Retrieve(QFile *mail, quint64 mailID)
{
    QString cmd = QString("RETR %1\r\n").arg(mailID);
    if(mail != NULL)
    {
        if(socket->write(cmd.toAscii()) == cmd.size())
        {
            if (socket->waitForBytesWritten())
            {
                if (socket->waitForReadyRead())
                {
                    QString mailInfo = socket->readLine();
                    if(mailInfo.startsWith("+OK"))
                    {
                        quint64 bytesCount = mailInfo.split(' ',QString::SkipEmptyParts).at(1).toULongLong();
                        quint64 bytesRead = 0;
                        if(mail->open(QIODevice::WriteOnly) && socket->waitForReadyRead())
                        {
                            while(bytesRead != bytesCount)
                            {
                                    QByteArray bytes = socket->readLine();
                                    if(bytes == ".\r\n")
                                    {
                                        break;
                                    }
                                    else if(bytes.size() != 0)
                                    {
                                        bytesRead += bytes.size();
                                        mail->write(bytes);
                                    }
                                    else
                                    {
                                        socket->waitForReadyRead();
                                    }
                                    qDebug(qPrintable(QString("Received %1 %").arg(1.0 * bytesRead * 100 / bytesCount)));
                            }
                            mail->close();
                            return true;
                        }
                        else
                        {
                            qDebug(qPrintable(mail->errorString()));
                            return false;
                        }
                    }
                    else
                    {
                        qDebug("Could not read.");
                        return false;
                    }
                }
                else
                {
                    qDebug("Can not get mail.");
                    return false;
                }
            }
            else
            {
                qDebug("Could not write bytes from buffer.");
                return false;
            }
        }
        else
        {
            qDebug("Could not write all bytes to server.");
            return false;
        }
    }
    else
    {
        return false;
    }
}

bool POP3Invoker::Quit()
{
    return execute("QUIT\r\n");
}

bool POP3Invoker::GetUIDL(quint64 mailID,QString &uidl)
{
    QString output;
    if(!execute(QString("UIDL %1\r\n").arg(mailID),&output))
    {
        return false;
    }
    QStringList separatedOutput = output.split(' ',QString::SkipEmptyParts);
    if (separatedOutput.size() == 3  && output.startsWith("+OK"))
    {
        uidl = separatedOutput.at(2);
        return true;
    }
    else
    {
        return false;
    }
}

bool POP3Invoker::execute(QString cmd,QString *output)
{
    if(socket->write(cmd.toAscii()) == cmd.size())
    {
        if (socket->waitForBytesWritten())
        {
            if (!socket->waitForReadyRead())
            {
                    qDebug("Could not read.");
                    return false;
            }
            QString result = socket->readAll();
            qDebug(qPrintable(result));
            if(output != NULL)
            {
                *output = result;
            }
            return result.startsWith("+OK");
        }
        else
        {
            qDebug("Could not write bytes from buffer.");
            return false;
        }
    }
    else
    {
        qDebug("Could not write all bytes to server.");
        return false;
    }
}

